home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / mail / mh / contrib / 9210 / stacknews.shar / stacknews next >
Text File  |  1992-10-22  |  13KB  |  490 lines

  1. #!/usr/bin/perl
  2. #
  3. # stacknews - create and maintain a folder stack for reading news
  4. #
  5. # This program builds a folder stack (separate from the MH folder stack)
  6. # of news groups to be read.  It prints the path to the appropriate
  7. # MH context file.  A separate MH context is kept for each news group.
  8. # This should speed up certain MH operations and avoids the problem of
  9. # having news group folders in your main MH context.
  10. #
  11. # Usage: 
  12. #    setenv MHCONTEXT \
  13. #        `stacknews [-(no)check] [-(no)clear] [-(no)debug] [-(no)list]
  14. #               [-(no)next] [-(no)push] [-(no)rigor] group ...`
  15. #
  16. # Options:
  17. #    -check        find the first group with new articles
  18. #    -clear        clear the folder stack
  19. #    -debug        turn on debugging
  20. #    -list        list the folder stack
  21. #    -next        pop the folder stack
  22. #    -nocheck    push all groups unconditionally
  23. #    -noclear    do not clear the folder stack
  24. #    -nodebug    operate in normal (non-debug) mode
  25. #    -nolist        do not list the folder stack
  26. #    -nonext        do not pop the folder stack
  27. #    -nopush        do not push groups onto the folder stack
  28. #    -norigor    use defaults or the environment ONLY
  29. #    -push        push groups onto the folder stack
  30. #    -rigor        check the MH profile (the usual MH stuff)
  31. #
  32. # Defaults: -check -noclear -nodebug -nonext -push -rigor
  33. #
  34. #        Any groups on the command line are pushed onto the stack.
  35. #        If no groups are specified, then all default groups are
  36. #        pushed onto the stack.
  37. #
  38. #        Mutually exclusive options: -clear, -list, -next, -push
  39. #
  40. # Some useful code to include in your .cshrc file:
  41. #
  42. #   if (! $?stacknews) then
  43. #     set stacknews = "stacknews"
  44. #   endif
  45. #   alias sninvo 'setenv MHCONTEXT `$stacknews \!*`'
  46. #   alias news 'set stacknews = "stacknews" ; sninvo'
  47. #   alias fnews 'set stacknews = "stacknews.f" ; sninvo'
  48. #   alias g 'setenv MHCONTEXT `$stacknews -next \!*`'
  49. #   alias q 'folder -fast last > /dev/null ; g'
  50. #   alias u 'setenv MHCONTEXT `$stacknews -nopush \!*`'
  51. #   alias z '$stacknews -clear \!*; unsetenv MHCONTEXT'
  52. #   alias nglist '$stacknews -list \!*'
  53. #
  54. # The code above defines the following commands:
  55. #    g    -- Go to the next news group.
  56. #    news    -- Read new news groups.
  57. #    nglist    -- See the current state of the news stack.
  58. #    q    -- Set the last article to be the current article
  59. #        --     and go to the next news group.
  60. #    sninvo    -- Utility alias to invoke stacknews.
  61. #    u    -- Set ("update") another shell's MHCONTEXT ...
  62. #            but unfortunately doesn't handle the case
  63. #            of an alternative set of news groups, e.g.
  64. #            as read by stacknews.f
  65. #    z    -- Stop reading news altogether.
  66. #
  67. #
  68. # Environment variables:
  69. #    HOME        path of home directory
  70. #    MH        path of MH profile file
  71. #    MHCONTEXT    path of current MH context file
  72. #    NEWSARTS     path of top directory containing news articles
  73. #    NEWSCONTEXTS    path of directory containing folder contexts
  74. #    NEWSGROUPS    list of colon-separated news groups to read
  75. #
  76. # MH profile components:
  77. #    context        path of MH context file
  78. #    path        path of MH directory
  79. #    (prog)        default options
  80. #    (prog)-arts    path of top directory containing news articles
  81. #    (prog)-contexts    path of directory containing folder contexts
  82. #    (prog)-defaults    path of file containing list of news groups
  83. #    (prog)-groups    list of comma-separated news groups to read
  84. #
  85. # Note: "(prog)" above is the invocation name of the program;
  86. #    MH profile components might be named stacknews-defaults, 
  87. #    stacknews-groups, and stacknews-arts, if the program is invoked
  88. #    as "stacknews".
  89. #
  90. # Files and Directories:
  91. #    $HOME/.mh_profile        default MH profile
  92. #    $HOME/.news-contexts        default news folder contexts directory
  93. #    $HOME/.news-contexts/Stack    news folder stack
  94. #    $HOME/.news-defaults        list of default news groups
  95. #    $HOME/Mail            default MH directory
  96. #    $HOME/Mail/context        default MH context file
  97. #
  98. # Author: Jerry Sweet <jns@fernwood.mpk.ca.us>
  99. # Taken from my old, similar, but much slower, "news" program.
  100. #
  101. # $Header: /q2/uh/jsweet/src/mh-front/RCS/stacknews,v 1.6 1992/06/11 18:24:52 jsweet Exp $
  102.  
  103. #
  104. # Subprograms
  105. #
  106.  
  107. sub relative_to_absolute {
  108.   local($_) = @_;
  109.  
  110.   if ($_ !~ /^\//) {
  111.     $_ = sprintf("%s%s%s", $mhroot, $_ ? '/' : '', $_);
  112.   }
  113.   if ($_ !~ /^\//) {
  114.     $_ = sprintf("%s/%s", $ENV{'HOME'}, $_);
  115.   }
  116.   $_;
  117. }
  118.  
  119. sub destroy_cur {
  120.   local($dir) = @_;
  121.   local($sequence_file) = "$dir/.mh_sequences";
  122.  
  123.   unlink $sequence_file if ($update_cur && -e $sequence_file);
  124.       # This is the only known way (in MH 6.6 at least) to reset cur if new
  125.       # messages have arrived in a folder (via rcvstore) after all messages
  126.       # were removed or refiled.
  127. }
  128.  
  129. #
  130. # Main
  131. #
  132.  
  133. umask 077;    # This should be adjustable, but isn't at the moment.
  134.  
  135. ($prog = $0) =~ s%.*/%%;
  136.  
  137. $newsarts = defined $ENV{'NEWSARTS'} ? $ENV{'NEWSARTS'} : '/usr/spool/news';
  138. $HOME = $ENV{'HOME'} ;
  139. $profile = defined $ENV{'MH'} ? $ENV{'MH'} : "$HOME/.mh_profile";
  140. $mhroot = "$HOME/Mail";
  141. if (defined $ENV{'NEWSGROUPS'}) {
  142.   @groups = split(/:/, $ENV{'NEWSGROUPS'});
  143. }
  144. $contexts = defined $ENV{'NEWSCONTEXTS'} ? 
  145.             $ENV{'NEWSCONTEXTS'} : 
  146.             "$HOME/.news-contexts";
  147. $mhcontext = defined $ENV{'MHCONTEXT'} ?
  148.             $ENV{'MHCONTEXT'} :
  149.             "$mhroot/context";
  150.  
  151. $check_folders = 1;
  152. $rigor = 1;
  153. $push = 1;
  154.  
  155. # Look for the -norigor option in the command line.
  156.  
  157. while ($_ = shift @ARGV) {
  158.   push(@Args, $_);
  159.   /^-norigor$/ && $rigor--;
  160.   /^-rigor$/   && $rigor++;
  161. }
  162.  
  163. if ($rigor > 0) {
  164.   $ndfile = -e "$HOME/.news-defaults" ? "$HOME/.news-defaults" : '';
  165.  
  166.   $profile_component = 'x-bogus';
  167.   
  168.   if (! open(PROFILE, "<$profile")) {
  169.     print STDERR "$prog: can't open profile \"$profile\" - $!\n";
  170.   }
  171.   else {
  172.     while (<PROFILE>) {
  173.       /^(\S+):\s*/ && do {
  174.           ($profile_component = $1) =~ tr/A-Z/a-z/;
  175.           $PROFILE_COMPONENT{$profile_component} = $';
  176.       };
  177.   
  178.       /^[ \t]/ && do {
  179.           $PROFILE_COMPONENT{$profile_component} .= $_;
  180.       };
  181.     }
  182.   
  183.     if (defined $PROFILE_COMPONENT{'path'}) {
  184.       $_ = $PROFILE_COMPONENT{'path'};
  185.       /^\s*(\S+)/ && do { $mhroot = $1; };
  186.     }
  187.     if (defined $PROFILE_COMPONENT{'context'}) {
  188.       $_ = $PROFILE_COMPONENT{'context'};
  189.       /^\s*(\S+)/ && do { 
  190.           $mhcontext = $1; 
  191.           if ($mhcontext !~ /^\//) {
  192.               $mhcontext = "$mhroot/$mhcontext";
  193.           }
  194.       };
  195.     }
  196.     if (defined $PROFILE_COMPONENT{$prog}) {
  197.       $_ = $PROFILE_COMPONENT{$prog};
  198.       @Args = (split, @Args);
  199.     }
  200.     if (defined $PROFILE_COMPONENT{"$prog-arts"}) {
  201.       $_ = $PROFILE_COMPONENT{"$prog-arts"};
  202.       /^\s*(\S+)/ && do { $newsarts = $1; };
  203.     }
  204.     if (defined $PROFILE_COMPONENT{"$prog-contexts"}) {
  205.       $_ = $PROFILE_COMPONENT{"$prog-contexts"};
  206.       /^\s*(\S+)/ && do { 
  207.           $contexts = $1; 
  208.           if ($contexts !~ /^\//) {
  209.               $contexts = "$mhroot/$contexts";
  210.           }
  211.       };
  212.     }
  213.     if (defined $PROFILE_COMPONENT{"$prog-defaults"}) {
  214.       $_ = $PROFILE_COMPONENT{"$prog-defaults"};
  215.       /^\s*(\S+)/ && do { 
  216.           $ndfile = $1; 
  217.           if ($contexts !~ /^\//) {
  218.               $contexts = "$mhroot/$contexts";
  219.           }
  220.       };
  221.     }
  222.     if (defined $PROFILE_COMPONENT{"$prog-groups"}) {
  223.       $_ = $PROFILE_COMPONENT{"$prog-groups"};
  224.       @profile_groups = split(/\s*,\s*/);
  225.     }
  226.   }
  227. }
  228.  
  229. # Evaluate the switches:
  230.  
  231. while ($_ = shift @Args) {
  232.   /^-check$/      && $check_folders++;
  233.   /^-clear$/      && $clear_stack++;
  234.   /^-debug$/      && $debug++;
  235.   /^-list$/      && $list++;
  236.   /^-next$/      && $next++;
  237.   /^-nocheck$/      && $check_folders--;
  238.   /^-noclear$/      && $clear_stack--;
  239.   /^-nodebug$/      && $debug--;
  240.   /^-nolist$/      && $list--;
  241.   /^-nonext$/      && $next--;
  242.   /^-nopush$/     && $push--;
  243.   /^-push$/       && $push++;
  244.   /^[^-]/      && push(@switch_groups, $_);
  245. }
  246.  
  247. if (! -d $contexts) {
  248.   if (! mkdir($contexts, 0700)) {
  249.     print STDERR "$prog: can't mkdir \"$contexts\" - $!\n";
  250.     exit 1;
  251.   }
  252. }
  253.  
  254.   
  255. $folder_stack = "$contexts/Stack";
  256.  
  257. if ($clear_stack > 0) {
  258.   @groups = ();
  259. }
  260. else {
  261.   # Read the folder stack:
  262.   
  263.   if (-e $folder_stack) {
  264.     if (! open(STACK, "<$folder_stack")) {
  265.       print STDERR 
  266.       "$prog: can't open folder stack file \"$folder_stack\" - $!\n";
  267.       print "$mhcontext\n" unless ! $print_context;
  268.       exit 1;
  269.     }
  270.     foreach (<STACK>) {
  271.       next if /^#|^\s*$/;
  272.       push(@stack, split);
  273.     }
  274.   }
  275.  
  276.   if ($list > 0) {
  277.     foreach (@stack) {
  278.       push(@group_list, $_);
  279.     }
  280.  
  281.     if ($#group_list >= 0) {
  282.       print STDERR join(' ', @group_list), "\n";
  283.     }
  284.     exit 0;
  285.   }
  286. }
  287.  
  288. shift(@stack) if ($next > 0);  # If we saw -next, then pop the stack.
  289.  
  290. if ($clear_stack <= 0 && $next <= 0 && $push > 0) {
  291.   # Decide on the news groups at which to look:
  292.  
  293.   if ($#switch_groups < 0 && $#profile_groups >= 0) {
  294.     push(@groups, @profile_groups);
  295.         # Select groups from the (prog)-groups: component.
  296.   }
  297.  
  298.   if ($#groups < 0 && $#switch_groups < 0 && $ndfile) {
  299.     # Look at the .news-defaults file:
  300.   
  301.     if (! open(NDFILE, $ndfile)) {
  302.       print STDERR "$prog: can't open news defaults file \"$ndfile\" - $!\n";
  303.       print "$mhcontext\n" unless ! $print_context;
  304.       exit 1;
  305.     }
  306.   
  307.     while (<NDFILE>) {
  308.       next if /^#|^\s*$/;
  309.       split;
  310.       push(@groups, @_);
  311.     }
  312.   }
  313.  
  314.   # Push news groups onto the folder stack:
  315.   
  316.   push(@stack, @groups);
  317.  
  318.   # Push groups from the command line onto the front of the stack:
  319.  
  320.   foreach (reverse(@switch_groups)) {
  321.     unshift(@stack, $_);
  322.   }
  323. }
  324.  
  325. if ($clear_stack <= 0 && $push > 0) {
  326.   # Find the first group for which new articles have arrived:
  327.  
  328.   if ($check_folders > 0) {
  329.     select(STDERR);
  330.     $| = 1;    # Force output of '.' as we look for a good group
  331.     select(STDOUT);
  332.   }
  333.  
  334.   $newsarts_cur = $newsarts;
  335.  
  336.   while ($#stack >= 0) {
  337.     ($_ = $stack[0]) =~ s/\s+//g;
  338.  
  339.     # An item beginning with '+' specifies a folder instead of
  340.     # a news group.
  341.  
  342.     if (/^\+/) {
  343.       $_ = $';
  344.       $group = $_;
  345.       $dir = &relative_to_absolute($_);
  346.       $folder = $_;
  347.       s%/%.%g;
  348.       $cur_context = "$contexts/FOLDER.$_";
  349.           # Bug: +foo.bar will have the same context as +foo/bar.
  350.       $update_cur = 1;
  351.     }
  352.     else  {
  353.       $group = $_;
  354.       s%\.%/%g;
  355.       $dir = "$newsarts_cur/$_";
  356.       $folder = $dir;
  357.       $cur_context = "$contexts/$group";
  358.       $update_cur = 0;
  359.     }
  360.  
  361.     # print STDERR "group  = \"$group\"\n", 
  362.     #          "dir    = \"$dir\"\n",
  363.     #          "folder = \"$folder\"\n",
  364.     #          "ctxt   = \"$cur_context\"\n",
  365.     #          "stack  = ", join(' ', @stack), "\n"
  366.     #     ;
  367.  
  368.     last if ($check_folders <= 0);
  369.  
  370.     if (! -d $dir) {
  371.       print STDERR "\nno such news group as $group\n";
  372.       shift @stack;
  373.       next;
  374.     }
  375.   
  376.     $ENV{'MHCONTEXT'} = $cur_context;
  377.     $finfo = `folder +$folder`;
  378.     if ($? >> 8) {
  379.       print STDERR "\nfolder change to +$folder failed\n";
  380.       shift @stack;
  381.       next;
  382.     }
  383.     print(STDERR $finfo) if ($debug > 0);
  384.   
  385.     if ($finfo =~ /\s+has\s+no\s+/) {
  386.       # There are no messages in this folder, so ensure that the cur
  387.       # sequence is destroyed and go on to the next folder.
  388.  
  389.       &destroy_cur($dir);
  390.       print STDERR '.';
  391.       $dots++;
  392.       shift @stack;
  393.       next;
  394.     }
  395.   
  396.     if ($finfo =~ /cur=\s*(\d+)/) {
  397.       $cur = $1;
  398.     }
  399.     else {
  400.       # There are messages in here, but there's no cur message apparent,
  401.       # so reset the cur sequence.
  402.  
  403.       &destroy_cur($dir);
  404.       $cur = 0;
  405.     }
  406.   
  407.     if ($finfo =~ /message.*\(\s*(\d+)-\s*(\d+)\s*\)/) {
  408.       $lastm = $2;
  409.   
  410.       if ($cur >= $lastm) {
  411.         # We don't have any new messages in this folder, so go on to
  412.         # the next one.  (Bug: we should check for an "unseen" sequence.)
  413.  
  414.         print STDERR '.';
  415.         $dots++;
  416.         shift @stack;
  417.         next;
  418.       }
  419.       else {
  420.         last;  # Found a live one.
  421.       }
  422.     }
  423.   }  # end while
  424.   
  425.   if ($dots > 0) {
  426.     print STDERR "\n";
  427.     select(STDERR);
  428.     $| = 0;
  429.     select(STDOUT);
  430.   }
  431. }  
  432.  
  433. # Rewrite the folder stack file:
  434.  
  435. if ($clear_stack > 0) {
  436.   @stack = ();
  437. }
  438.  
  439. if ($push > 0) {
  440.   if (! open(STACK, ">$folder_stack")) {
  441.     print STDERR 
  442.       "$prog: can't rewrite folder stack file \"$folder_stack\" - $!\n";
  443.     print "$mhcontext\n" unless ! $print_context;
  444.     exit 1;
  445.   }
  446.   if ( (! print STACK join(' ', @stack)) || ! close(STACK) ) {
  447.     print STDERR 
  448.       "$prog: can't write folder stack file \"$folder_stack\" - $!\n";
  449.     print "$mhcontext\n" unless ! $print_context;
  450.     exit 1;
  451.   }
  452. }
  453.  
  454. if ($clear_stack > 0) {
  455.   exit 0;
  456. }
  457.  
  458. # Tell the user what the state of the folder stack is:
  459.  
  460. if ($#stack >= 0) {
  461.   $_ = $stack[0];
  462.  
  463.   if ($next <= 0) {
  464.     printf(STDERR "%d news group%s selected; topmost: %s\n", 
  465.           $#stack + 1,
  466.           $#stack > 0 ? 's' : '',
  467.           $_);
  468.   }
  469.   else {
  470.     print STDERR "topmost: $_\n";
  471.   }
  472.  
  473.   if (/^\+/) {
  474.     $_ = $';
  475.     s%/%.%g;
  476.     print "$contexts/FOLDER.$_\n";
  477.   }
  478.   else {
  479.     print "$contexts/$_\n";
  480.   }
  481. }
  482. else {
  483.   if ($next <= 0) {
  484.     print STDERR "no news groups selected\n";
  485.   }
  486.   print "$mhcontext\n" unless ! $print_context;
  487. }
  488.  
  489. exit 0;
  490.